/*
 * logging.cpp
 *
 *  Created on: 2010/08/27
 *      Author: zjumty@gmail.com
 */

#include <cstdarg>
#include <cstdio>
#include <sstream>
#include "logging.h"

#ifdef LOG_FORMAT_A
  #undef LOG_FORMAT_A
#endif

using namespace std;

const string logging::LEVEL_DEBUG("D");
const string logging::LEVEL_ERROR("E");
const string logging::LEVEL_INFO("I");
const string logging::LEVEL_WARN("W");

logging *logging::instance = NULL;


struct log_functor
{
    block_queue<string> *que;
    ofstream *out;

    void operator ()()
    {
        while(true) {
            shared_ptr<string> msg = que->get();
            if(msg.get() != NULL){
                (*out) << *(msg) << flush;
            } else {
                break;
            }
        }
    }
};

logging::logging()
    :log_que(1000), is_sync(true)
{
    log_functor logout;
    logout.out = &(this->out);
    logout.que = &(this->log_que);
    this->log_thead = new boost::thread(logout);
}

logging::~logging()
{
    this->log_que.dispose();
    this->log_thead->join();
    this->close_out();
    delete this->log_thead;
}

int logging::init()
{
    if(logging::instance != NULL){
        delete logging::instance;
    }
    logging::instance = new logging();
    return 0;
}

void logging::set_locale(std::locale loc)
{
    this->out.imbue(loc);
}


void logging::set_file(const string& filename)
{
    this->out.open(filename.c_str(), ios::out | ios::app);
}

void logging::set_sync(bool sync)
{
    this->is_sync = sync;
}

void logging::close_out()
{
    if(this->out.is_open()){
        this->out.close();
    }
}

logging* logging::get()
{
    return logging::instance;
}

void logging::finalize()
{
    if(logging::instance != NULL){
        delete logging::instance;
    }
}

#define LOG_FORMAT_A(FMT, LEVEL) \
    va_list ap; \
    char buf[1024]; \
    memset(buf, 0, 1024); \
    va_start(ap, FMT); \
    vsprintf(buf, fmt, ap); \
    log(LEVEL, buf); \
    va_end(ap);

void logging::debug( const char* text )
{
#ifdef _DEBUG
    log(LEVEL_DEBUG, text);
#endif
}

void logging::debug_f(const char* fmt, ...)
{
#ifdef _DEBUG
    LOG_FORMAT_A(fmt, LEVEL_DEBUG)
#endif
}

void logging::error( const char* text )
{
    log(LEVEL_ERROR, text);
}

void logging::error_f( const char* fmt, ... )
{
    LOG_FORMAT_A(fmt, LEVEL_ERROR)
}

void logging::info( const char* text )
{
    log(LEVEL_INFO, text);
}

void logging::info_f( const char* fmt, ... )
{
    LOG_FORMAT_A(fmt, LEVEL_INFO)
}

void logging::warn( const char* text )
{
    log(LEVEL_WARN, text);
}

void logging::warn_f( const char* fmt, ... )
{
    LOG_FORMAT_A(fmt, LEVEL_WARN)
}

// ログ出力
inline void logging::log(const string& level, const char* text)
{
    if(this->out.is_open()){
        using namespace boost::posix_time;
        using namespace boost::gregorian;

        ptime now = second_clock::local_time();
        stringstream ss;
        ss << "[" << level << "] " << to_iso_extended_string(now) << " " << text << endl;
        if(ss.fail()){
            cerr << "logging error." << endl;
            return;
        }
        if(!this->is_sync){
            string *msg = new string(ss.str());
            this->log_que.put(boost::shared_ptr<string>(msg));
        } else {
            boost::unique_lock<mutex> lck(this->log_guard);
            this->out << ss.str() << flush;
        }
    }
}

